#pragma once

#include <deque>
#include <mutex>
#include <memory>
#include <condition_variable>

// 线程安全阻塞队列
template<class T, class Container = std::deque<T>>
class BlockingQueue {
public:
    typedef Container container_type;
    typedef typename Container::value_type value_type;
    typedef typename Container::size_type size_type;
    typedef typename Container::const_reference const_reference;

public:
    // 构造无大小限制的阻塞队列
    BlockingQueue() : max_size(-1) {};

    // 构造限制大小的阻塞队列
    explicit BlockingQueue(size_t size) : max_size(size) {}

    BlockingQueue(const BlockingQueue& other) {
        std::lock_guard<std::mutex> lk(other.mutex);
        c = other.c;
        max_size = other.max_size;
    }

    BlockingQueue& operator=(const BlockingQueue& other) {
        std::lock_guard<std::mutex> lk(other.mutex);
        c = other.c;
        max_size = other.max_size;
        return *this;
    }

    bool empty() const {
        std::lock_guard<std::mutex> lk(mutex);
        return c.empty();
    }

    void clear() {
        std::lock_guard<std::mutex> lk(mutex);
        c.clear();
    }

    size_t size() const {
        std::lock_guard<std::mutex> lk(mutex);
        return c.size();
    }

    const_reference front() const {
        std::lock_guard<std::mutex> lk(mutex);
        return c.front();
    }

    const_reference back() const {
        std::lock_guard<std::mutex> lk(mutex);
        return c.back();
    }

    void push(const value_type& x) {
        std::unique_lock<std::mutex> lk(mutex);
        if (max_size == -1 || max_size > c.size()) {
            c.push_back(x);
        } else {
            cond_notFull.wait(lk, [this]{ return max_size > c.size(); });
            c.push_back(x);
        }
        cond_notEmpty.notify_one();
    }

    void wait_and_pop(value_type& x) {
        std::unique_lock<std::mutex> lk(mutex);
        cond_notEmpty.wait(lk, [this]{ return !c.empty(); });
        x = c.front();
        c.pop_front();
        cond_notFull.notify_one();
    }

    std::shared_ptr<value_type> wait_and_pop() {
        std::unique_lock<std::mutex> lk(mutex);
        cond_notEmpty.wait(lk, [this]{ return !c.empty(); });
        std::shared_ptr<value_type> res(std::make_shared<value_type>(c.front()));
        c.pop_front();
        cond_notFull.notify_one();
        return res;
    }

    bool try_pop(value_type& x) {
        std::lock_guard<std::mutex> lk(mutex);
        if (c.empty()) return false;
        x = c.front();
        c.pop_front();
        cond_notFull.notify_one();
        return true;
    }

    std::shared_ptr<value_type> try_pop() {
        std::lock_guard<std::mutex> lk(mutex);
        if (c.empty()) return std::shared_ptr<value_type>();
        std::shared_ptr<value_type> res(std::make_shared<value_type>(c.front()));
        c.pop_front();
        cond_notFull.notify_one();
        return res;
    }

protected:
    Container c;
    mutable std::mutex mutex;
    std::condition_variable cond_notEmpty;
    std::condition_variable cond_notFull;
    int64_t max_size;

private:
    template<class U, class Sequence>
    friend bool operator==(const BlockingQueue<U, Sequence>& x, const BlockingQueue<U, Sequence>& y);

    template<class U, class Sequence>
    friend bool operator<(const BlockingQueue<U, Container>& x, const BlockingQueue<U, Sequence>& y);
};

template<class U, class Sequence>
bool operator== (const BlockingQueue<U, Sequence>& x, const BlockingQueue<U, Sequence>& y) {
    std::unique_lock<std::mutex> lk_x(x.mutex, std::defer_lock);
    std::unique_lock<std::mutex> lk_y(y.mutex, std::defer_lock);
    std::lock(lk_x, lk_y);
    return x.c == y.c;
}

template<class U, class Sequence>
bool operator< (const BlockingQueue<U, Sequence>& x, const BlockingQueue<U, Sequence>& y) {
    std::unique_lock<std::mutex> lk_x(x.mutex, std::defer_lock);
    std::unique_lock<std::mutex> lk_y(y.mutex, std::defer_lock);
    std::lock(lk_x, lk_y);
    return x.c < y.c;
}